package main

import (
	"fmt"
	"time"
)

// 从示例 6.13 fibonacci.go 的斐波那契程序开始，制定解决方案，使斐波那契周期计算独立到协程中，并可以把结果发送给通道。
// 结束的时候关闭通道。main() 函数读取通道并打印结果：goFibonacci.go

// 使用练习 6.9 fibonacci2.go 中的算法写一个更短的 gofibonacci2.go

// 使用 select 语句来写，并让通道退出 (gofibonacci_select.go)

// 注意：当给结果计时并和 6.13 对比时，我们发现使用通道通信的性能开销有轻微削减；这个例子中的算法使用协程并非性能最好的选择；
// 但是 gofibonacci3 方案使用了 2 个协程带来了 3 倍的提速。

var list = []uint64{1, 1}

func gofibo1(n int) chan uint64 {
	res := make(chan uint64)
	go func() {
		if n <= 0 {
			res <- 1
		} else if len(list) > n {
			res <- list[n]
		} else {
			n1 := <-gofibo1(n - 1)
			n2 := <-gofibo1(n - 2)
			val := n1 + n2
			res <- val
			list = append(list, val)

		}
		close(res)
	}()

	return res
}

func gofibo2(n int, ch chan uint64) {
	x, y := uint64(1), uint64(1)
	for i := 0; i < n; i++ {
		ch <- x
		x, y = y, x+y
	}
	close(ch)
}

func gofibo3(ch chan uint64, quit chan bool) {
	x, y := uint64(1), uint64(1)
	for {
		select {
		case ch <- x:
			x, y = y, x+y
		case <-quit:
			return
		}
	}
}

func main() {
	n := 20
	ts := time.Now().UnixMicro()

	for i := 0; i < n; i++ {
		fmt.Println(<-gofibo1(i))
	}
	td := time.Now().UnixMicro()
	fmt.Println("cost time: ", td-ts)

	c := make(chan uint64, n)
	go gofibo2(n, c)
	for i := 0; i < n; i++ {
		fmt.Println(<-c)
	}
	tc := time.Now().UnixMicro()
	fmt.Println("cost time: ", tc-td)

	// 使用select方法
	ch := make(chan uint64)
	quit := make(chan bool)
	go gofibo3(ch, quit)
	for i := 0; i < n; i++ {
		fmt.Println(<-ch)
	}
	// 接收完N个数之后中断协程
	quit <- true
	fmt.Println("cost time: ", time.Now().UnixMicro()-tc)
}
